package com.egghiro.framework.j2se.util.hash;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * 文字列暗号化<br>
 * Hash函数
 * 
 */
public class Encrypter {

    /**
     * メッセージダイジェスト：MD5
     */
    private static final String ALG_MD5 = "MD5";

    /**
     * メッセージダイジェスト：SHA-1
     */
    private static final String ALG_SHA1 = "SHA-1";

    /**
     * メッセージダイジェスト：SHA-256
     */
    private static final String ALG_SHA256 = "SHA-256";

    /**
     * メッセージダイジェスト：SHA-384
     */
    private static final String ALG_SHA384 = "SHA-384";

    /**
     * メッセージダイジェスト：SHA-512
     */
    private static final String ALG_SHA512 = "SHA-512";
    
    /**
     * 入力された文字列のMD5ハッシュ値を得る。
     * 
     * @param input
     *            文字列
     * @return ハッシュ値
     */
    public static String md5(String input) {
        return hash(input, ALG_MD5);
    }

    /**
     * 入力された文字列のSHA-256ハッシュ値を得る。
     * 
     * @param input
     *            文字列
     * @return ハッシュ値
     */
    public static String sha256(String input) {
        return hash(input, ALG_SHA256);
    }

    /**
     * ハッシュ値を計算する。<br>
     * <br>
     * もしこのメソッドを公開する(publicにする)場合、NoSuchAlgorithmExceptionをthrowする必要がある。
     * (""を返すのはhashの機能にならないため)<br>
     * だが、client側の例外処理で結局""にする可能性が高いため、このメソッドを公開しないほうがいい。
     * @param input
     *            入力文字列
     * @param algorithm
     *            使用するハッシュアルゴリズム
     * @return
     */
    private static String hash(String input, String algorithm) {
        MessageDigest digest = null;
        try {
            digest = MessageDigest.getInstance(algorithm);
            // digest.reset();
            digest.update(input.getBytes());
        } catch (NoSuchAlgorithmException e) {
            return "";
        }
        return byte2HexString(digest.digest());
    }

    /**
     * バイトコードを16進数文字列に変換
     * 
     * @param input
     * @return
     */
    public static String byte2HexString(byte[] inputByteArray) {

        StringBuffer buff = new StringBuffer();
//        int count = inputByteArray.length;
//        for (int i = 0; i < count; i++) {
//            buff.append(Integer.toHexString((inputByteArray[i] >> 4) & 0x0F));
//            buff.append(Integer.toHexString(inputByteArray[i] & 0x0F));
//        }
        for (byte inputByte : inputByteArray) {
            /*
             * 解説：& 0xFFの理由：
             * toHexStringはint型を引数としているので、byte型は一旦intに変換される。
             * byteはプラス(+)の場合に問題はないが、マイナス(-)の場合、
             * intに変換されたら先頭に1111...(3バイト分)が追加される
             * それをtoHexStringで変換すると1byteの先頭に3byte分のff⇒「ffffff」が追加される。
             * そこで、 「& 0xFF」で計算すると、先頭の1111...が0になるのでffも追加されない。
             */
            buff.append(Integer.toHexString(inputByte & (int)0xFF));
            /*
             * 補足：
             * 「&」の計算時、もし実行環境が先に「&」を計算してからbyteをintに変換する場合、
             * (つまり、0xFFをintではなくbyte型として認識する場合)「&」処理はムダになる。
             * その場合は下の1のようにcastするか、または2のように4bitずつ計算すればOK。
             * 念のため上の処理にcastを追加した。
             */
//            buff.append(Integer.toHexString(inputByte & (int)0xFF));//1
//          buff.append(Integer.toHexString((inputByte >> 4) & 0x0F));//2
//          buff.append(Integer.toHexString(inputByte & 0x0F));//2
        }
        return buff.toString();
    }
    
//    public static void main(String [] args){
//        String string ="string";
//        System.out.println(md5(string));
//    }
}
